#pragma once

#include <unordered_map>
#include <mutex>
#include <atomic>
#include <cstdarg>
#include "format.hpp"
#include "sink.hpp"
#include "looper.hpp"


namespace bitlog
{
    class Logger
    {
        protected:
            std::string _logger_name;
            std::atomic<LogLevel::lev> _limit_level;
            Formatter::ptr _formatter;
            std::vector<LogSink::ptr> _sinks;
            std::mutex _mutex;

        public:
            using ptr = std::shared_ptr<Logger>;

            Logger(const std::string &logger_name, LogLevel::lev limit_level, Formatter::ptr &formatter, std::vector<LogSink::ptr> sinks)
                : _logger_name(logger_name), _limit_level(limit_level), _formatter(formatter), _sinks(sinks.begin(), sinks.end())
            { }

            const std::string &name()
            {
                return _logger_name;
            }

            void debug(const std::string &filename, size_t line, const std::string &fmt, ...)
            {
                // 判断当前的日志是否达到了输出等级
                if(_limit_level > LogLevel::lev::DEBUG)
                {
                    return;
                }

                // 对 fmt 格式化字符串和不定参数进行组织，得到日志消息字符串
                va_list ap;
                va_start(ap, fmt);
                char *res;
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1)
                {
                    std::cout << "vasprintf failed!" << std::endl;
                    return;
                }
                va_end(ap);

                // 构造LogMsg对象
                LogMsg msg(LogLevel::lev::DEBUG, filename, line, _logger_name, res);

                // 通过格式化工具对LogMsg进行格式化，得到格式化之后的字符串
                std::string strmsg = _formatter->format(msg);

                // 进行日志落地
                log(strmsg.c_str(), strmsg.size());
                free(res);
            }

            void info(const std::string &filename, size_t line, const std::string &fmt, ...)
            {
                // 判断当前的日志是否达到了输出等级
                if(_limit_level > LogLevel::lev::INFO)
                {
                    return;
                }

                // 对 fmt 格式化字符串和不定参数进行组织，得到日志消息字符串
                va_list ap;
                va_start(ap, fmt);
                char *res;
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1)
                {
                    std::cout << "vasprintf failed!" << std::endl;
                    return;
                }
                va_end(ap);

                // 构造LogMsg对象
                LogMsg msg(LogLevel::lev::INFO, filename, line, _logger_name, res);

                // 通过格式化工具对LogMsg进行格式化，得到格式化之后的字符串
                std::string strmsg = _formatter->format(msg);

                // 进行日志落地
                log(strmsg.c_str(), strmsg.size());
                free(res);
            }

            void warn(const std::string &filename, size_t line, const std::string &fmt, ...)
            {
                // 判断当前的日志是否达到了输出等级
                if(_limit_level > LogLevel::lev::WARN)
                {
                    return;
                }

                // 对 fmt 格式化字符串和不定参数进行组织，得到日志消息字符串
                va_list ap;
                va_start(ap, fmt);
                char *res;
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1)
                {
                    std::cout << "vasprintf failed!" << std::endl;
                    return;
                }
                va_end(ap);

                // 构造LogMsg对象
                LogMsg msg(LogLevel::lev::WARN, filename, line, _logger_name, res);

                // 通过格式化工具对LogMsg进行格式化，得到格式化之后的字符串
                std::string strmsg = _formatter->format(msg);

                // 进行日志落地
                log(strmsg.c_str(), strmsg.size());
                free(res);
            }

            void error(const std::string &filename, size_t line, const std::string &fmt, ...)
            {
                // 判断当前的日志是否达到了输出等级
                if(_limit_level > LogLevel::lev::ERROR)
                {
                    return;
                }

                // 对 fmt 格式化字符串和不定参数进行组织，得到日志消息字符串
                va_list ap;
                va_start(ap, fmt);
                char *res;
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1)
                {
                    std::cout << "vasprintf failed!" << std::endl;
                    return;
                }
                va_end(ap);

                // 构造LogMsg对象
                LogMsg msg(LogLevel::lev::ERROR, filename, line, _logger_name, res);

                // 通过格式化工具对LogMsg进行格式化，得到格式化之后的字符串
                std::string strmsg = _formatter->format(msg);

                // 进行日志落地
                log(strmsg.c_str(), strmsg.size());
                free(res);
            }

            void fatal(const std::string &filename, size_t line, const std::string &fmt, ...)
            {
                // 判断当前的日志是否达到了输出等级
                if(_limit_level > LogLevel::lev::FATAL)
                {
                    return;
                }

                // 对 fmt 格式化字符串和不定参数进行组织，得到日志消息字符串
                va_list ap;
                va_start(ap, fmt);
                char *res;
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1)
                {
                    std::cout << "vasprintf failed!" << std::endl;
                    return;
                }
                va_end(ap);

                // 构造LogMsg对象
                LogMsg msg(LogLevel::lev::FATAL, filename, line, _logger_name, res);

                // 通过格式化工具对LogMsg进行格式化，得到格式化之后的字符串
                std::string strmsg = _formatter->format(msg);

                // 进行日志落地
                log(strmsg.c_str(), strmsg.size());
                free(res);
            }

            // 抽象接口完成实际的落地输出，不同的日志器会有不同的实际落地方式
            virtual void log(const char *data, size_t len) = 0;
    };

    class SyncLogger : public Logger
    {
        public:
            SyncLogger(const std::string &logger_name, LogLevel::lev limit_level, Formatter::ptr &formatter, std::vector<LogSink::ptr> sinks)
            : Logger(logger_name, limit_level, formatter, sinks)
            { }

            void log(const char *data, size_t len)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                if(_sinks.empty())
                {
                    return;
                }

                for(auto &sink : _sinks)
                {
                    sink->log(data, len);
                }
            }
    };

    class AsyncLogger : public Logger
    {
        private:
            AsyncLooper::ptr _looper;

        public:
            AsyncLogger(const std::string &logger_name, LogLevel::lev limit_level, Formatter::ptr &formatter, std::vector<LogSink::ptr> sinks)
            : Logger(logger_name, limit_level, formatter, sinks), 
            _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1)))
            { } 

            // 将数据写入缓冲区
            void log(const char *data, size_t len)
            {
                _looper->push(data, len);
            }

            // 将缓冲区中的数据落地
            void realLog(Buffer &buf)
            {
                if(_sinks.empty())
                {
                    return;
                }
                for(auto &sink : _sinks)
                {
                    sink->log(buf.begin(), buf.readAbleSize());
                }
            }
    };


    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };
    // 使用建造者模式来建造日志器
    class LoggerBuilder
    {
        protected:
            LoggerType _logger_type;
            std::string _logger_name;
            LogLevel::lev _limit_level;
            Formatter::ptr _formatter;
            std::vector<LogSink::ptr> _sinks;

        public:
            LoggerBuilder()
                : _logger_type(LoggerType::LOGGER_SYNC), _limit_level(LogLevel::lev::DEBUG)
            { }

            void buildLoggerType(LoggerType type)
            {
                _logger_type = type;
            }

            void buildLoggerName(const std::string &name)
            {
                _logger_name = name;
            }

            void buildLoggerLevel(LogLevel::lev level)
            {
                _limit_level = level;
            }

            void buildFormatter(const std::string &pattern)
            {
                _formatter = std::make_shared<Formatter>(pattern);
            }

            template<typename SinkType, typename ...Args>
            void buildSink(Args &&...args)
            {
                LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
                _sinks.push_back(psink);
            }

            virtual Logger::ptr build() = 0;
    };

    // 局部日志器
    class LocalLoggerBuilder : public LoggerBuilder
    {
        public:
            Logger::ptr build() override
            {
                assert(!_logger_name.empty());
                if(_formatter.get() == nullptr)
                {
                    _formatter = std::make_shared<Formatter>();
                }
                if(_sinks.empty())
                {
                    buildSink<StdoutSink>();
                }
                if(_logger_type == LoggerType::LOGGER_ASYNC)
                {
                    return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
                }

                return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
    };



    // 日志器管理器 (懒汉单例模式)
    class LoggerManager
    {
        private:
            std::mutex _mutex;
            Logger::ptr _root_logger; // 默认日志器
            std::unordered_map<std::string, Logger::ptr> _loggers;
        
        public:
            static LoggerManager &getInstance()
            {
                // 在c++11之后，对于静态局部变量，编译器在编译的层面实现了线程安全
                // 当静态局部变量在没有被构造完成之前，其他的线程进入就会被阻塞
                static LoggerManager eton;
                return eton;
            }

            void addLogger(Logger::ptr &logger)
            {
                if(hasLogger(logger->name())) return;
                std::unique_lock<std::mutex> lock(_mutex);
                _loggers.insert(std::make_pair(logger->name(), logger));
            }

            bool hasLogger(const std::string &name)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _loggers.find(name);
                if(it  == _loggers.end())
                {
                    return false;
                }

                return true;
            }

            Logger::ptr getLogger(const std::string &name)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _loggers.find(name);
                if(it  == _loggers.end())
                {
                    return Logger::ptr();
                }

                return it->second;
            }

            Logger::ptr rootLogger()
            {
                return _root_logger;
            }

        private:
            LoggerManager()
            {
                std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
                builder->buildLoggerName("root");
                _root_logger = builder->build();
                _loggers.insert(std::make_pair("root", _root_logger));
            }
    };
    
    // 全局日志器
    class GlobalLoggerBuilder : public LoggerBuilder
    {
        public:
            Logger::ptr build() override
            {
                assert(!_logger_name.empty());
                if(_formatter.get() == nullptr)
                {
                    _formatter = std::make_shared<Formatter>();
                }
                if(_sinks.empty())
                {
                    buildSink<StdoutSink>();
                }

                Logger::ptr logger;
                if(_logger_type == LoggerType::LOGGER_ASYNC)
                {
                    logger =  std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
                }else
                {
                    logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
                }
                LoggerManager::getInstance().addLogger(logger);

                return logger;
            }
    };
}